﻿#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//5.赋值运算符重载
//5.1 运算符重载
//C++为了增强代码的可读性引入了运算符重载，运算符重载是具有特殊函数名的函数，也具有其
//返回值类型，函数名字以及参数列表，其返回值类型与参数列表与普通的函数类似。
//函数名字为：关键字operator后面接需要重载的运算符符号。
//函数原型：返回值类型 operator操作符(参数列表)
//注意：
//不能通过连接其他符号来创建新的操作符：比如operator@
//重载操作符必须有一个自定义类型参数
//用于内置类型的运算符，其含义不能改变，例如：内置的整型 + ，不 能改变其含义
//作为类成员函数重载时，其形参看起来比操作数数目少1，因为成员函数的第一个参数为隐藏的this
//不能改变操作符的操作数 个数。一个操作符有几个操作数，重载时就有几个参数	
//.*  :: sizeof  ?:  . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

//class Date {
//public:
//	Date(int year = 1, int month = 1, int day = 1) {
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//	void Print() {
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//	//private:
//	int _year;
//	int _month;
//	int _day;
//};
//
////bool riqixiaoyu(const Date& x1, const Date& x2)
////bool Compare1(const Date& x1, const Date& x2)
////为了避免出现上面函数名不明确，且提高代码可读性（cout << d1 < d2 << endl;），所以引出了运算符重载
////编译器并不知道自定义类型怎么比较，所以需要自己实现（全局版）
//bool operator<(const Date& x1, const Date& x2) { //传值传参要去调用拷贝构造，还要开辟空间，所以用传引用传参
//	if (x1._year < x2._year) {
//		return true;
//	}
//	else if (x1._year == x2._year && x1._month < x2._month) {
//		return true;
//	}
//	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day) {
//		return true;
//	}
//	else {
//		return false;
//	}
//}
//
//int main() {
//	Date d1(2024, 8, 19);
//	Date d2(2023, 7, 18);
//	//d1 < d2;//不能用这个运算符比较日期大小。所以需要一个比较函数
//	cout << (d1 < d2) << endl;//这里比较需要加括号，因为<<流运算符优先级更高
//	cout << (operator<(d1, d2)) << endl;//显示调用。operator加<就是函数名
//	return 0;
//}



////待解决问题：成员变量不在是私有
////将比较函数改为成员函数
//class Date {
//public:
//	Date(int year = 1, int month = 1, int day = 1) {
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//	void Print() {
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//
//
//	//bool operator<(const Date& x1, const Date& x2) { //错误版本
//	//成员函数的签名中，运算符重载函数只有一个参数，因为左侧的对象隐式地作为 this 被传递。
//	//这里的 operator< 函数有两个参数，这在成员函数中是不允许的。
//	
//	// bool operator<(Date* this, const Date& x2)
//	// 这里需要注意的是，左操作数是this，指向调用函数的对象
//	bool operator<(const Date& x2) {
//		if (_year < x2._year) {
//			return true;
//		}
//		else if (_year == x2._year && _month < x2._month) {
//			return true;
//		}
//		else if (_year == x2._year && _month == x2._month && _day < x2._day) {
//			return true;
//		}
//		else {
//			return false;
//		}
//	}
//
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//
//int main() {
//	Date d1(2024, 8, 19);
//	Date d2(2023, 7, 18);
//	cout << (d1 < d2) << endl;
//	cout << (d1.operator<(d2)) << endl;
//
//	return 0;
//}



//涉及作业部分
class Date {
public:
	Date(int year = 1, int month = 1, int day = 1) {
		_year = year;
		_month = month;
		_day = day;
	}

	void Print() {
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	bool operator<(const Date& x2) {
		if (_year < x2._year) {
			return true;
		}
		else if (_year == x2._year && _month < x2._month) {
			return true;
		}
		else if (_year == x2._year && _month == x2._month && _day < x2._day) {
			return true;
		}
		else {
			return false;
		}
	}

	bool operator==(const Date& x2) {
		return _year == x2._year && _month == x2._month && _day == x2._day;
	}

	//d1 <= d2
	//这里不需要再拷贝<的函数后修改，复用上面的<和==

	//在C++中，可以通过利用已定义的运算符重载函数来实现其他比较运算符的重载。
	//这种做法的原理在于逻辑关系和运算符之间的联系。
	//当定义了基础的运算符重载函数（如 < 和 == ），其他运算符（如 <= , >, >= , != ）可以通过组合这些基础运算符来实现。
	//这样可以避免重复的代码，实现更高效、更易维护的比较操作。
	bool operator<=(const Date& x2) {
		//this不能用实参显示的传，也不能写形参，但是可以在类里面用
		//这里的this是d1的地址
		return *this < x2 || *this == x2;
	}

	bool operator>(const Date& x2) {
		return !(*this <= x2);//<=逻辑取反就是 >
	}

	bool operator>=(const Date& x2) {
		return !(*this < x2);//>=逻辑取反就是 <
	}

	bool operator!=(const Date& x2) {
		return !(*this == x2);//==逻辑取反就是 !=
	}

	int GetMonthDay(int year, int month) {
		int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		//要先判断是否为2月，否则每次都会先判是否为断闰年，没意义
		if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
			return 29;
		return monthArray[month];
	}

	//日期加天数
	//Date operator+(int day) { //这里实现+=。如果是+，那么该对象也变了
	Date& operator+=(int day) { //出了该函数，对象还在。如果返回Date，会生成一份拷贝，拷贝又要调用拷贝构造。所以返回引用
		//月进位
		//先加天数，并判断是否需要进位
		_day += day;//_day是目前天数，day是加上的天数
		//如果目前天数大于目前年份月份天数，那么要将现在的天数减去该月天数，并且月份进位
		//重复上述步骤，直到目前天数符合该月份要求
		while (_day > GetMonthDay(_year, _month)) {
			_day -= GetMonthDay(_year, _month);
			_month++;
			//年进位
			if (_month == 13) {
				_year++;
				_month = 1;
			}
		}
		return *this;//返回调用该函数的Date类的对象(this是指针)
	}

	Date operator+(int day) { //实现正确的日期+天数
		////因为不能改变自己（即调用该函数的对象），所以要调用拷贝构造函数拷贝一份
		//Date tmp(*this);
		//tmp._day += day;
		//while (tmp._day > GetMonthDay(tmp._year, tmp._month)) {
		//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
		//	tmp._month++;
		//	//年进位
		//	if (tmp._month == 13) {
		//		tmp._year++;
		//		tmp._month = 1;
		//	}
		//}
		//return tmp;//tmp出了该函数作用域就不在了，所以是传值返回

		//复用版本
		Date tmp(*this);
		tmp += day;
		return tmp;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2024, 8, 19);
	Date d2(2023, 7, 18);
	cout << (d1 < d2) << endl;
	cout << (d1 == d2) << endl;

	//Date ret = d1 + 100;//要实现+=。如果是+，那么该对象(d1)也变了
	Date ret1 = d1 += 50;
	ret1.Print();
	d1.Print();

	Date ret2 = d2 + 50;
	ret2.Print();
	d2.Print();
	return 0;
}